<?php

namespace App\Containers\AppSection\Moneybox\Actions;

use App\Containers\AppSection\Moneybox\Tasks\CreateMoneyboxTask;
use App\Containers\AppSection\Moneybox\Tasks\SendDataTask;
use App\Containers\AppSection\Moneybox\Tasks\UpdateMoneyboxTask;
use App\Ship\Cryptopro\CryptoHelper;
use App\Ship\Exceptions\InternalErrorException;
use App\Ship\Parents\Actions\SubAction;

class SendDataSubAction extends SubAction
{
    private CryptoHelper $crypto;
    const ALGORITHM = "sha256";

    public function __construct()
    {
        $this->crypto = new CryptoHelper();
    }

    public function run(string $uuid, string $method, array $request = [])
    {
        $json_request = json_encode($request, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
        if ($json_request === false) {
            throw new InternalErrorException("json_encode error");
        }

        $sha256 = hash(self::ALGORITHM, $json_request);

        $moneybox = app(CreateMoneyboxTask::class)->run([
            'request' => $request,
            'uuid' => $uuid,
            'method' => $method
        ]);

        try {
            $certificates = $this->crypto->GetCertificates();
            if (count($certificates) === 0) {
                throw new InternalErrorException("GetCertificates error");
            }
        } catch (\Throwable $e) {
            throw new InternalErrorException($e->getMessage());
        }

        $hash = "";
        if ($json_request != "") {
            try {
                $hash = $this->crypto->Sign($certificates[0], $sha256);
                if ($hash === false) {
                    throw new InternalErrorException("Sign error");
                }
                $hash = str_replace("\n", "", $hash);
                $hash = str_replace("\r", "", $hash);
            } catch (\Throwable $e) {
                throw new InternalErrorException($e->getMessage());
            }
        }

        try {
            $response = app(SendDataTask::class)->run($moneybox->id, $hash, $json_request);
        } catch (\Throwable $e) {
            app(UpdateMoneyboxTask::class)->run($moneybox->id, [
                "request_time" => now(),
                "response" => $e->getMessage(),
                "response_code" => $e->getCode(),
                "response_time" => now(),
                "status" => 0,
            ]);
            throw new InternalErrorException($e->getMessage());
        }

        app(UpdateMoneyboxTask::class)->run($moneybox->id, [
            "request_time" => now(),
            "response_code" => $response->getStatusCode(),
            "status" => 2,
        ]);
    }
}
